package org.xxx.epms.gateway.security.filter;

import cn.hutool.core.text.AntPathMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;
import org.xxx.epms.common.utils.JwtTokenUtil;
import org.xxx.epms.gateway.config.service.AdminDetailsService;
import org.xxx.epms.gateway.config.service.InspectorDetailsService;
import org.xxx.epms.gateway.security.component.DynamicSecurityMetadataSource;
import org.xxx.epms.gateway.security.config.IgnoreUrlsConfig;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * 自定义的鉴权服务，通过鉴权的才能继续访问某个请求
 */
@Component
public class MyRBACServiceWebFlux implements ReactiveAuthorizationManager<AuthorizationContext> {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    private final String TOKEN_HEADER = "Authorization";
    private final String TOKEN_HEAD = "Bearer ";

    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    private AdminDetailsService adminDetailsService;
    @Autowired
    private InspectorDetailsService inspectorDetailsService;

    private Map<String, UserDetailsService> userDetailsServiceMap;
    private void initUserDetailsServiceMap(){
        userDetailsServiceMap = new HashMap<String,UserDetailsService>();
        userDetailsServiceMap.put("adm",this.adminDetailsService);
        userDetailsServiceMap.put("ins",this.inspectorDetailsService);
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
        ServerHttpRequest request = object.getExchange().getRequest();
        AntPathMatcher antPathMatcher = new AntPathMatcher();

        //OPTIONS请求直接放行，允许跨域请求
        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
            return Mono.just(new AuthorizationDecision(true));
        }
        //白名单请求直接放行
        PathMatcher pathMatcher = new org.springframework.util.AntPathMatcher();
        for (String path : ignoreUrlsConfig.getUrls()) {
            if(pathMatcher.match(path,request.getURI().getPath())){
                return Mono.just(new AuthorizationDecision(true));
            }
        }
        //自定义鉴权处理
        initUserDetailsServiceMap();
        String authHeader = request.getHeaders().getFirst(TOKEN_HEADER);
        String token = authHeader.substring(TOKEN_HEAD.length());
        String username = jwtTokenUtil.getUserNameFromToken(token);
        String userType = jwtTokenUtil.getUserTypeFromToken(token);
        //加载当前请求所需的权限
        List<ConfigAttribute> configAttributes = dynamicSecurityMetadataSource.getAttributes(request.getPath().toString());
        if(configAttributes.isEmpty()){ //如果没有配置权限，则默认放行
            return Mono.just(new AuthorizationDecision(true));
        }
        //加载用户权限
        UserDetails userDetails = this.userDetailsServiceMap.get(userType).loadUserByUsername(username);
        Logger.getLogger(getClass().getName()).info("当前用户：" + username + "，权限：" + userDetails.getAuthorities());
        //遍历权限，判断是否有访问所需资源的权限
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //将访问所需资源或用户拥有资源进行比对
            String needAuthority = configAttribute.getAttribute();
            for (GrantedAuthority grantedAuthority : userDetails.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    Logger.getLogger(getClass().getName()).info("用户权限认证通:：" + needAuthority);
                    return Mono.just(new AuthorizationDecision(true));
                }
            }
        }
        Logger.getLogger(getClass().getName()).info("用户权限认证失败:" + configAttributes);
        return Mono.just(new AuthorizationDecision(false));
    }

    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
        return null;
    }
}
